﻿// Copyright (c) 2021 raoyutian Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Emgu.CV;
using System.Drawing;
using PaddleOCR.Onnx.Paddle;
using System.Drawing.Imaging;

namespace PaddleOCR.Onnx
{
    /// <summary>
    /// PaddleOCR识别引擎
    /// </summary>
    public class PaddleOCREngine : IDisposable
    {
        private OCREngine engine { get; set; }
        private float scale = 1.0f;
        /// <summary>
        /// 初始化OCR引擎
        /// </summary>
        /// <param name="config">模型配置</param>
        /// <param name="parameter">识别参数</param>
        public PaddleOCREngine(OCRModelConfig config, OCRParameter parameter = null)
        {
            if (parameter == null) parameter = new OCRParameter();
            if (config == null)
            {
                string root = System.IO.Path.GetDirectoryName(typeof(OCRModelConfig).Assembly.Location);
                config = new OCRModelConfig();
                string modelPathroot = root + @"\inference";
                config.det_infer = modelPathroot + @"\ch_PP-OCRv3_det_infer.onnx";
                config.rec_infer = modelPathroot + @"\ch_PP-OCRv3_rec_infer.onnx";
                config.cls_infer = modelPathroot + @"\ch_ppocr_mobile_v2.0_cls_infer.onnx";
                config.keys = modelPathroot + @"\ppocr_keys.txt";
            }
            if(engine==null) engine = new OCREngine(config.det_infer, config.cls_infer, config.rec_infer, config.keys, parameter);
           
        }

        /// <summary>
        ///文本识别
        /// </summary>
        /// <param name="mat">Mat图像</param>
        /// <returns>OCR识别结果</returns>
        public OCRResult DetectText(Mat mat)
        {
            if (mat == null) throw new ArgumentNullException("mat");

            #region 小图执行放大，分段线性缩放

            if (mat.Width <= 50 || mat.Height <= 50)
            {
                scale = 3;
            }
           else if (mat.Width <= 100 || mat.Height <= 100)
            {
                scale = 2;
            }
            else if (mat.Width <= 150 || mat.Height <= 150)
            {
                scale = 1.5f;
            }
            Mat resizemat = new Mat();
            CvInvoke.Resize(mat, resizemat, new Size( Convert.ToInt32(mat.Width* scale), Convert.ToInt32(mat.Height * scale)));
            #endregion
            OCRResult result = engine.Detect(resizemat);

            #region 返回结果的区域需要还原比例
            int padding = 50;
            if (engine.Parameter != null) padding = engine.Parameter.Padding;
                foreach (var item in result.TextBlocks)
                {
                List<Point> boxPoints = new List<Point>();
                
                for (int i = 0; i < item.BoxPoints.Count; i++)
                    {
                     var point = item.BoxPoints[i];
                    Point p = new Point();
                    p.X = Convert.ToInt32(((float)(point.X- padding)) / scale);
                    p.Y = Convert.ToInt32(((float)(point.Y- padding)) / scale);
                    if (p.X < 0) p.X = 0;
                    if (p.Y < 0) p.Y = 0;
                    boxPoints.Add(p);
                    }
                item.BoxPoints = boxPoints;
                }
            #endregion
            return result;
        }
        /// <summary>
        ///文本识别
        /// </summary>
        /// <param name="filename">bitmap文件</param>
        /// <returns>OCR识别结果</returns>
        public OCRResult DetectText(string filename)
        {
            return DetectText(File.ReadAllBytes(filename));
        }
        /// <summary>
        ///文本识别
        /// </summary>
        /// <param name="image">bitmap对象</param>
        /// <returns>OCR识别结果</returns>
        public OCRResult DetectText(Image image)
        {
            if (image == null) throw new ArgumentNullException("image");
            var imagebyte = ImageToBytes(image);
            return DetectText(imagebyte);
        }
        /// <summary>
        ///文本识别
        /// </summary>
        /// <param name="imagebyte">图像内存流</param>
        /// <returns>OCR识别结果</returns>
        public OCRResult DetectText(byte[] imagebyte)
        {
            if (imagebyte == null) throw new ArgumentNullException("imagebyte");
           Mat mat =new Mat();
            CvInvoke.Imdecode(imagebyte, Emgu.CV.CvEnum.ImreadModes.Color, mat);
           return DetectText(mat);
        }
        /// <summary>
        ///文本识别
        /// </summary>
        /// <param name="imagebase64">图像base64</param>
        /// <returns>OCR识别结果</returns>
        public OCRResult DetectTextBase64(string imagebase64)
        {
            
            if (imagebase64 == null || imagebase64 == "") throw new ArgumentNullException("imagebase64");
            byte[] imagebyte=Convert.FromBase64String(imagebase64);
            return DetectText(imagebyte);
        }

        /// <summary>
        /// Convert Image to Byte[]
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        private byte[] ImageToBytes(Image image)
        {
            ImageFormat format = image.RawFormat;
            using (MemoryStream ms = new MemoryStream())
            {
                if (format.Guid == ImageFormat.Jpeg.Guid)
                {
                    image.Save(ms, ImageFormat.Jpeg);
                }
                else if (format.Guid == ImageFormat.Png.Guid)
                {
                    image.Save(ms, ImageFormat.Png);
                }
                else if (format.Guid == ImageFormat.Bmp.Guid)
                {
                    image.Save(ms, ImageFormat.Bmp);
                }
                else if (format.Guid == ImageFormat.Gif.Guid)
                {
                    image.Save(ms, ImageFormat.Gif);
                }
                else if (format.Guid == ImageFormat.Icon.Guid)
                {
                    image.Save(ms, ImageFormat.Icon);
                }
                else
                {
                    image.Save(ms, ImageFormat.Png);
                }
                byte[] buffer = new byte[ms.Length];
                ms.Seek(0, SeekOrigin.Begin);
                ms.Read(buffer, 0, buffer.Length);
                return buffer;
            }
        }
        public void Dispose()
        {
            engine.Dispose();
        }
    }
}
